
import java.util.List;
import java.util.ArrayList;


public class Lines {
	//this class serves as the main memory of the program,in which contains all source code read from file.
	
	
	/**
	 * A list that stores all the lines read from input file.
	 * 
	 */
	private static List<Instruction> lines = new ArrayList<Instruction>();

	private static Integer startingLoc = null;
	private static String segName = null;
	private static int segSize = 0;
	private static int locCtr;
	private static boolean relocatable = false;
	private static boolean hasORIG = false;//flag if source file has .ORIG at beginning.

	public static void setORIG() {
		//set when first line of source file has .ORIG
		hasORIG = true;
	}

	public static boolean hasORIG() {
		return hasORIG;
	}

	public static void setRel() {
		//set when source code is relocatable.
		relocatable = true;
	}

	public static boolean isRel() {
		return relocatable;
	}

	public static void setLC(int val) {
		//increment location counter by val.
		locCtr += val;
	}

	public static int getLC() {
		//get current location counter.
		return locCtr;
	}

	public static void stStartingLoc(int i) {
		startingLoc = i;
	}

	public static Integer getStartingLoc() {
		return startingLoc;
	}

	public static void stSegName(String name) {
		segName = name;

	}

	public static String getSegName() {
		return segName;
	}

	public static void add(Instruction line) {
		lines.add(line);
	}

	public static Instruction get(int index) {
		return lines.get(index);
	}

	public static int size() {
		return lines.size();
	}

	public static void calcSegSize() throws AssembleException {
		segSize = locCtr - startingLoc;
		if(segSize>65535 && relocatable) {
			throw new AssembleException(0,"Relocatable program must not be larger then one page.");
		}
	}

	public static int getSegSize() {
		return segSize;
	}

	public static void checkOverflow() throws AssembleException {

		// this method checks overflow and undefined symbol errors.
		for (int i = 0; i < lines.size(); i++) {
			String ops = lines.get(i).getOps();
			String operand[] = lines.get(i).getOperand();
			Integer lineNum = lines.get(i).getLineNum();

			// check for operand overflow
			if (ops.equals("ADD") || ops.equals("AND")) {
				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}
				if (isOverflow(operand[1], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[1], "R0",
							"R7");
				}
				if (operand[2].charAt(0) == 'R') {
					if (isOverflow(operand[2], "reg", lineNum)) {
						throw new AssembleException(5, lineNum, operand[2],
								"R0", "R7");
					}
				} else if (operand[2].charAt(0) == '#') {
					if (isOverflow(operand[2], "imm5", lineNum)) {
						throw new AssembleException(5, lineNum, operand[2],
								"-16", "15");
					}
				} else if (operand[2].charAt(0) == 'x') {
					if (isOverflow(operand[2], "imm5", lineNum)) {
						throw new AssembleException(5, lineNum, operand[2],
								"x0", "x1F");
					}
				} else {
				//	Integer val = SymbTable.getVal(operand[2]);
					if (isOverflow(operand[2], "imm5", lineNum)) {
						throw new AssembleException(5, lineNum, operand[2],
								"x0", "x1F or #-16 and #15.");
					}
				}

			} else if (ops.substring(0, 2).equals("BR")) {
				if (isOverflow(operand[0], "addr", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "#0",
							"#65535");
				}

			} else if (ops.equals("JSR") || ops.equals("JMP")) {
				if (isOverflow(operand[0], "addr", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "#0",
							"#65535");
				}
			} else if (ops.equals("JSRR") || ops.equals("JMPR")) {
				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}

				if (isOverflow(operand[1], "index6", lineNum)) {
					throw new AssembleException(5, lineNum, operand[1], "#0",
							"#63");
				}
			} else if (ops.equals("LD")) {

				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}
				if (operand[1].charAt(0) != '=') {
					if (isOverflow(operand[1], "addr", lineNum)) {
						throw new AssembleException(5, lineNum, operand[1],
								"#0", "#65535");
					}
				} else {
					String lit = operand[1].substring(1);

					if (isOverflow(lit, "lit", lineNum)) {
						if (lit.charAt(0) == 'x') {
							throw new AssembleException(5, lineNum, operand[1],
									"x0", "xFFFF");
						} else {
							throw new AssembleException(5, lineNum, operand[1],
									"#-32768", "#-32767");
						}
					}
				}

			} else if (ops.equals("LDI") || ops.equals("LEA")
					|| ops.equals("ST") || ops.equals("STI")) {
				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}

				if (isOverflow(operand[1], "addr", lineNum)) {
					throw new AssembleException(5, lineNum, operand[1], "#0",
							"#65535");
				}

			} else if (ops.equals("LDR") || (ops.equals("STR"))) {
				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}

				if (isOverflow(operand[1], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[1], "R0",
							"R7");
				}

				if (isOverflow(operand[2], "index6", lineNum)) {
					throw new AssembleException(5, lineNum, operand[2], "#0",
							"#63");
				}
			} else if (ops.equals("NOT")) {
				if (isOverflow(operand[0], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "R0",
							"R7");
				}

				if (isOverflow(operand[1], "reg", lineNum)) {
					throw new AssembleException(5, lineNum, operand[1], "R0",
							"R7");
				}
			} else if (ops.equals("TRAP")) {
				if (isOverflow(operand[0], "trapvect8", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "#0",
							"#255");
				}
			} else if (ops.equals(".BLKW")) {
				if (isOverflow(operand[0], "blkw", lineNum)) {
					throw new AssembleException(5, lineNum, operand[0], "#1",
							"#65535");
				}

			} else if (ops.equals(".FILL")) {
				if (operand[0].charAt(0) == '#') {
					if (isOverflow(operand[0], "fill", lineNum)) {
						throw new AssembleException(5, lineNum, operand[0],
								"#-32768", "#32767");
					}
				} else if (operand[0].charAt(0) == 'x') {
					if (isOverflow(operand[0], "fill", lineNum)) {
						throw new AssembleException(5, lineNum, operand[0],
								"x0", "xFFFF");
					}
				} else {
					//Integer val = SymbTable.getVal(operand[0]);
					if (isOverflow(operand[0], "fill", lineNum)) {
						throw new AssembleException(5, lineNum, operand[0],
								"x0", "xFFFF or #-32768 and #32767.");
					}
				}
			} else if (ops.equals(".END")) {
				if (!operand[0].equals("")) {
					if (isOverflow(operand[0], "addr", lineNum)) {
						throw new AssembleException(5, lineNum, operand[0],
								"x0", "xFFFF");
					}
				}

			}
		}

	}

	//depending on the type of the value,decide if the value of operand is out of range.
	private static boolean isOverflow(String operand, String type,
			Integer lineNum) throws AssembleException {
		if(!EXTTable.hasSymb(operand)) {
			if (type.equals("reg")) {
				if (operand.charAt(0) == 'R') {
					try {
						int dr = Integer.parseInt(operand.substring(1));
						if (dr > 7 || dr < 0) {
							return true;
						}
					} catch (NumberFormatException ex) {
						throw new AssembleException(0,
								"Register is not legal at line " + lineNum
										+ ".Register is falsely written as "
										+ operand);
					}
	
				} else {
	
					Integer val = SymbTable.getVal(operand);
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
					if (val > 7 || val < 0) {
						return true;
					}
				}
	
			} else if (type.equals("addr")) {
				char ch = operand.charAt(0);
				if (ch == '#' || ch == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 65535 || val < 0) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					//check undefined symbol error.
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
	
					if (val < 0 || val > 65535) {
						return true;
					} else {
						return false;
					}
				}
	
			} else if (type.equals("imm5")) {
	
				char ch = operand.charAt(0);
				if (ch == '#') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 15 || val < -16) {
						return true;
					} else {
						return false;
					}
				} else if (ch == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val < 0 || val > 31) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					//check undefined symbol error.
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
					if (val < -16 || val > 31) {
						return true;
					} else {
						return false;
					}
				}
	
			} else if (type.equals("index6")) {
				char ch = operand.charAt(0);
				if (ch == '#' || ch == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 63 || val < 0) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					//check undefined symbol error.
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
					if (val < 0 || val > 63) {
						return true;
					} else {
						return false;
					}
				}
	
			} else if (type.equals("lit")) {
				if (operand.charAt(0) == '#') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 32767 || val < -32768) {
						return true;
					} else {
						return false;
					}
				} else if (operand.charAt(0) == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 65535 || val < 0) {
						return true;
					} else {
						return false;
					}
				}
	
			} else if (type.equals("trapvect8")) {
				char ch = operand.charAt(0);
				if (ch == '#' || ch == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 255 || val < 0) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					//check undefined symbol error.
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
					if (val < 0 || val > 255) {
						return true;
					} else {
						return false;
					}
				}
			} else if (type.equals("blkw")) {
				char ch = operand.charAt(0);
				if (ch == '#' || ch == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 65535 || val < 1) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					//check undefined symbol error.
					if (val == null) {
						throw new AssembleException(7, lineNum, operand);
					}
					if (val < 1 || val > 65535) {
						return true;
					} else {
						return false;
					}
				}
			} else if (type.equals("fill")) {
				if (operand.charAt(0) == '#') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 32767 || val < -32768) {
						return true;
					} else {
						return false;
					}
				} else if (operand.charAt(0) == 'x') {
					Integer val = Utilities.toDecimal(operand);
					if (val > 65535 || val < 0) {
						return true;
					} else {
						return false;
					}
				} else {
					Integer val = SymbTable.getVal(operand);
					if (val > 65535 || val < -32768) {
						return true;
					} else {
						return false;
					}
				}
			} else if (type.equals("")) {
	
			}
		}
		return false;

	}

}
